<?php

namespace Xin\Setting\Laravel;

use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Xin\Setting\DatabaseRepository;
use Xin\Setting\Exceptions\NotFountSettingItemException;
use Xin\Support\Arr;

class ModelRepository extends DatabaseRepository
{
	use WithSearch;

	/**
	 * @inerhitDoc
	 */
	public function all(array $search = [], array $options = [])
	{
		$options = array_merge([
			'order' => 'sort',
		], $options);

		$items = $this->newQueryWithSearch($search, $options)->get();

		return $this->optimizeItems($items);
	}

	/**
	 * @inerhitDoc
	 */
	public function paginate(array $search = [], array $options = [])
	{
		// 获取字段
		$defaultFields = [
			"id", "title", "name", "alias", "type", "group", "remark", "sort",
			"status", "system", "display", "public", "updated_at", "created_at",
		];
		$options = array_merge([
			'order' => 'id desc',
			'default_fields' => $defaultFields,
		], $options);

		/** @var LengthAwarePaginator $result */
		$result = $this->newQueryWithSearch($search, $options)->paginate(
			Arr::get($options, 'limit', Arr::get($options, 'page_size', Arr::get($options, 'perPage', 15),),),
			Arr::get($options, 'page', 1)
		);

		return $result->through(function (Model $item) {
			$value = $this->serializer->unserialize($item['type'], $item['value'], $isVarChanged);
			if ($isVarChanged) {
				$item['original_value'] = $item['value'];
				$item['value'] = $value;
			}

			return $item;
		})->toArray();
	}

	/**
	 * @inerhitDoc
	 */
	protected function getExistsKeys(array $keys)
	{
		return $this->newQuery()->whereIn('name', $keys)->pluck('name')->toArray();
	}

	/**
	 * @inerhitDoc
	 */
	protected function create(array $data, bool $serialize = false)
	{
		$builder = $this->newQuery();

		// 是否需要更新值
		if (isset($data['value']) && $serialize) {
			$data['value'] = $this->serializer->serialize($data['type'], $data['value']);
		}

		return $builder->forceCreate($data)->toArray();
	}

	/**
	 * @inerhitDoc
	 * @throws NotFountSettingItemException
	 */
	protected function update($key, array $data, bool $serialize = false)
	{
		// 构建查询器
		$builder = $this->newQuery();

		// 查询数据库数据
		/** @var DatabaseSetting $info */
		$info = $builder->where('name', $key)->first();
		if (empty($info)) {
			throw new NotFountSettingItemException($key);
		}

		// 是否需要更新值
		if (isset($data['value']) && $serialize) {
			$type = $data['type'] ?? $info->type;
			$data['value'] = $this->serializer->serialize($type, $data['value']);
		}

		// 更新数据
		$info->forceFill($data)->save();

		return $info->toArray();
	}

	/**
	 * @inerhitDoc
	 */
	public function infos(array $keys)
	{
		$items = $this->newQuery()->whereIn('name', $keys)->get();

		return $this->optimizeItems($items);
	}

	/**
	 * 优化返回的结果集
	 * @param Collection $items
	 * @return array
	 */
	protected function optimizeItems(Collection $items)
	{
		return $items->reduce(function ($result, Model $item) {
			$item = $item->toArray();

			$value = $this->serializer->unserialize($item['type'], $item['value'], $isVarChanged);
			if ($isVarChanged) {
				$item['original_value'] = $item['value'];
				$item['value'] = $value;
			}

			$result[$item['name']] = $item;

			return $result;
		}, []);
	}

	/**
	 * @inerhitDoc
	 */
	public function deletes(array $keys)
	{
		$builder = $this->newQuery();

		// 查询要删除的数据是否存在
		return $builder->whereIn('name', $keys)->get()->map(function (Model $item) {
			$item->forceDelete();

			return $item->toArray();
		});
	}

	/**
	 * @inerhitDoc
	 */
	public function getMultiple($keys, $default = null)
	{
		$builder = $this->newQuery();
		$result = $builder->whereIn('name', $keys)->pluck('value', 'name')->toArray();

		foreach ($keys as $key) {
			if (!isset($result[$key])) {
				$result[$key] = $default;
			}
		}

		return $result;
	}

	/**
	 * @inerhitDoc
	 */
	public function setMultiple(array $settings)
	{
		$keys = array_keys($settings);

		return $this->newQuery()->whereIn('name', $keys)->get()->reduce(function ($result, Model $item) use ($settings) {
			$item = $item->toArray();
			$key = $item['name'];

			$value = $this->serializer->serialize($item['type'], $settings[$key]);

			$this->newQuery()->where('name', $key)->update([
				'value' => $value,
			]);

			$settings[$key] = $value;

			return $settings;
		}, []);
	}

	/**
	 * 构造一个新的查询器
	 * @return EloquentBuilder
	 */
	protected function newQuery()
	{
		if ($modelClass = $this->getConfig('model')) {
			return call_user_func([$modelClass, 'query']);
		}

		return DatabaseSetting::query();
	}

	/**
	 * 支持搜索的查询器
	 * @param array $search
	 * @return EloquentBuilder
	 */
	protected function newQueryWithSearch($search = [], array $options = [])
	{
		$query = $this->newQuery();
		if (method_exists($query, 'search')) {
			$query = $query->search($search);
		} else {
			$searchFields = $this->getSearchFields();
			$query = $this->withSearch($this->newQuery(), $searchFields, $search);
		}

		// 获取关联数据
		if ($options['with'] ?? null) {
			$query->with($options['with']);
		}

		// 排序
		if ($options['order'] ?? null) {
			$orders = $this->resolveOrders($options['order']);
			foreach ($orders as $order) {
				$query->orderBy($order[0], $order[1] ?? 'asc');
			}
		}

		// 获取字段
		if ($options['fields'] ?? null) {
			$defaultFields = $options['default_fields'] ?? [];
			if (is_array($options['fields'])) {
				$fields = $options['fields'];
			} elseif (is_callable($options['fields'])) {
				$fields = call_user_func($options['fields'], $defaultFields);
			}
			$query->select($fields);
		}


		return $query;
	}
}
